// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/websockets/websocket_deflate_stream.h"

#include <stddef.h>
#include <stdint.h>

#include <deque>
#include <iterator>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "net/base/completion_callback.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/websockets/websocket_deflate_parameters.h"
#include "net/websockets/websocket_deflate_predictor.h"
#include "net/websockets/websocket_deflater.h"
#include "net/websockets/websocket_frame.h"
#include "net/websockets/websocket_inflater.h"
#include "net/websockets/websocket_stream.h"
#include "net/websockets/websocket_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {
namespace {

    typedef ::testing::MockFunction<void(int)> MockCallback; // NOLINT
    using ::testing::_;
    using ::testing::InSequence;
    using ::testing::Invoke;
    using ::testing::Return;

    typedef uint32_t FrameFlag;
    const FrameFlag kNoFlag = 0;
    const FrameFlag kFinal = 1;
    const FrameFlag kReserved1 = 2;
    // We don't define values for other flags because we don't need them.

    // The value must equal to the value of the corresponding
    // constant in websocket_deflate_stream.cc
    const size_t kChunkSize = 4 * 1024;
    const int kWindowBits = 15;

    scoped_refptr<IOBuffer> ToIOBuffer(const std::string& s)
    {
        scoped_refptr<IOBuffer> buffer = new IOBuffer(s.size());
        memcpy(buffer->data(), s.data(), s.size());
        return buffer;
    }

    std::string ToString(IOBufferWithSize* buffer)
    {
        return std::string(buffer->data(), buffer->size());
    }

    std::string ToString(const scoped_refptr<IOBufferWithSize>& buffer)
    {
        return ToString(buffer.get());
    }

    std::string ToString(IOBuffer* buffer, size_t size)
    {
        return std::string(buffer->data(), size);
    }

    std::string ToString(const scoped_refptr<IOBuffer>& buffer, size_t size)
    {
        return ToString(buffer.get(), size);
    }

    std::string ToString(const WebSocketFrame* frame)
    {
        return frame->data.get() ? ToString(frame->data, frame->header.payload_length)
                                 : "";
    }

    std::string ToString(const std::unique_ptr<WebSocketFrame>& frame)
    {
        return ToString(frame.get());
    }

    void AppendTo(std::vector<std::unique_ptr<WebSocketFrame>>* frames,
        WebSocketFrameHeader::OpCode opcode,
        FrameFlag flag,
        const std::string& data)
    {
        std::unique_ptr<WebSocketFrame> frame(new WebSocketFrame(opcode));
        frame->header.final = (flag & kFinal);
        frame->header.reserved1 = (flag & kReserved1);
        frame->data = ToIOBuffer(data);
        frame->header.payload_length = data.size();
        frames->push_back(std::move(frame));
    }

    void AppendTo(std::vector<std::unique_ptr<WebSocketFrame>>* frames,
        WebSocketFrameHeader::OpCode opcode,
        FrameFlag flag)
    {
        std::unique_ptr<WebSocketFrame> frame(new WebSocketFrame(opcode));
        frame->header.final = (flag & kFinal);
        frame->header.reserved1 = (flag & kReserved1);
        frames->push_back(std::move(frame));
    }

    class MockWebSocketStream : public WebSocketStream {
    public:
        MOCK_METHOD2(ReadFrames,
            int(std::vector<std::unique_ptr<WebSocketFrame>>*,
                const CompletionCallback&));
        MOCK_METHOD2(WriteFrames,
            int(std::vector<std::unique_ptr<WebSocketFrame>>*,
                const CompletionCallback&));
        MOCK_METHOD0(Close, void());
        MOCK_CONST_METHOD0(GetSubProtocol, std::string());
        MOCK_CONST_METHOD0(GetExtensions, std::string());
    };

    // This mock class relies on some assumptions.
    //  - RecordInputDataFrame is called after the corresponding WriteFrames
    //    call.
    //  - RecordWrittenDataFrame is called before writing the frame.
    class WebSocketDeflatePredictorMock : public WebSocketDeflatePredictor {
    public:
        WebSocketDeflatePredictorMock()
            : result_(DEFLATE)
        {
        }
        ~WebSocketDeflatePredictorMock() override
        {
            // Verify whether all expectaions are consumed.
            if (!frames_to_be_input_.empty()) {
                ADD_FAILURE() << "There are missing frames to be input.";
                return;
            }
            if (!frames_written_.empty()) {
                ADD_FAILURE() << "There are extra written frames.";
                return;
            }
        }

        // WebSocketDeflatePredictor functions.
        Result Predict(const std::vector<std::unique_ptr<WebSocketFrame>>& frames,
            size_t frame_index) override
        {
            return result_;
        }
        void RecordInputDataFrame(const WebSocketFrame* frame) override
        {
            if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)) {
                ADD_FAILURE() << "Control frames should not be recorded.";
                return;
            }
            if (frame->header.reserved1) {
                ADD_FAILURE() << "Input frame may not be compressed.";
                return;
            }
            if (frames_to_be_input_.empty()) {
                ADD_FAILURE() << "Unexpected input data frame";
                return;
            }
            if (frame != frames_to_be_input_.front()) {
                ADD_FAILURE() << "Input data frame does not match the expectation.";
                return;
            }
            frames_to_be_input_.pop_front();
        }
        void RecordWrittenDataFrame(const WebSocketFrame* frame) override
        {
            if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)) {
                ADD_FAILURE() << "Control frames should not be recorded.";
                return;
            }
            frames_written_.push_back(frame);
        }

        // Sets |result_| for the |Predict| return value.
        void set_result(Result result) { result_ = result; }

        // Adds |frame| as an expectation of future |RecordInputDataFrame| call.
        void AddFrameToBeInput(const WebSocketFrame* frame)
        {
            if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode))
                return;
            frames_to_be_input_.push_back(frame);
        }
        // Verifies that |frame| is recorded in order.
        void VerifySentFrame(const WebSocketFrame* frame)
        {
            if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode))
                return;
            if (frames_written_.empty()) {
                ADD_FAILURE() << "There are missing frames to be written.";
                return;
            }
            if (frame != frames_written_.front()) {
                ADD_FAILURE() << "Written data frame does not match the expectation.";
                return;
            }
            frames_written_.pop_front();
        }
        void AddFramesToBeInput(
            const std::vector<std::unique_ptr<WebSocketFrame>>& frames)
        {
            for (size_t i = 0; i < frames.size(); ++i)
                AddFrameToBeInput(frames[i].get());
        }
        void VerifySentFrames(
            const std::vector<std::unique_ptr<WebSocketFrame>>& frames)
        {
            for (size_t i = 0; i < frames.size(); ++i)
                VerifySentFrame(frames[i].get());
        }
        // Call this method in order to disable checks in the destructor when
        // WriteFrames fails.
        void Clear()
        {
            frames_to_be_input_.clear();
            frames_written_.clear();
        }

    private:
        Result result_;
        // Data frames which will be recorded by |RecordInputFrames|.
        // Pushed by |AddFrameToBeInput| and popped and verified by
        // |RecordInputFrames|.
        std::deque<const WebSocketFrame*> frames_to_be_input_;
        // Data frames recorded by |RecordWrittenFrames|.
        // Pushed by |RecordWrittenFrames| and popped and verified by
        // |VerifySentFrame|.
        std::deque<const WebSocketFrame*> frames_written_;

        DISALLOW_COPY_AND_ASSIGN(WebSocketDeflatePredictorMock);
    };

    class WebSocketDeflateStreamTest : public ::testing::Test {
    public:
        WebSocketDeflateStreamTest()
            : mock_stream_(NULL)
            , predictor_(NULL)
        {
        }
        ~WebSocketDeflateStreamTest() override { }

        void SetUp() override
        {
            Initialize(WebSocketDeflater::TAKE_OVER_CONTEXT, kWindowBits);
        }

    protected:
        // Initialize deflate_stream_ with the given parameters.
        void Initialize(WebSocketDeflater::ContextTakeOverMode mode,
            int window_bits)
        {
            WebSocketDeflateParameters parameters;
            if (mode == WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT) {
                parameters.SetClientNoContextTakeOver();
            }
            parameters.SetClientMaxWindowBits(window_bits);
            mock_stream_ = new testing::StrictMock<MockWebSocketStream>;
            predictor_ = new WebSocketDeflatePredictorMock;
            deflate_stream_.reset(new WebSocketDeflateStream(
                std::unique_ptr<WebSocketStream>(mock_stream_), parameters,
                std::unique_ptr<WebSocketDeflatePredictor>(predictor_)));
        }

        std::unique_ptr<WebSocketDeflateStream> deflate_stream_;
        // Owned by |deflate_stream_|.
        MockWebSocketStream* mock_stream_;
        // Owned by |deflate_stream_|.
        WebSocketDeflatePredictorMock* predictor_;
    };

    // Since WebSocketDeflater with DoNotTakeOverContext is well tested at
    // websocket_deflater_test.cc, we have only a few tests for this configuration
    // here.
    class WebSocketDeflateStreamWithDoNotTakeOverContextTest
        : public WebSocketDeflateStreamTest {
    public:
        WebSocketDeflateStreamWithDoNotTakeOverContextTest() { }
        ~WebSocketDeflateStreamWithDoNotTakeOverContextTest() override { }

        void SetUp() override
        {
            Initialize(WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT, kWindowBits);
        }
    };

    class WebSocketDeflateStreamWithClientWindowBitsTest
        : public WebSocketDeflateStreamTest {
    public:
        WebSocketDeflateStreamWithClientWindowBitsTest() { }
        ~WebSocketDeflateStreamWithClientWindowBitsTest() override { }

        // Overridden to postpone the call to Initialize().
        void SetUp() override { }

        // This needs to be called explicitly from the tests.
        void SetUpWithWindowBits(int window_bits)
        {
            Initialize(WebSocketDeflater::TAKE_OVER_CONTEXT, window_bits);
        }

        // Add a frame which will be compressed to a smaller size if the window
        // size is large enough.
        void AddCompressibleFrameString()
        {
            const std::string word = "Chromium";
            const std::string payload = word + std::string(256, 'a') + word;
            AppendTo(&frames_, WebSocketFrameHeader::kOpCodeText, kFinal, payload);
            predictor_->AddFramesToBeInput(frames_);
        }

    protected:
        std::vector<std::unique_ptr<WebSocketFrame>> frames_;
    };

    // ReadFrameStub is a stub for WebSocketStream::ReadFrames.
    // It returns |result_| and |frames_to_output_| to the caller and
    // saves parameters to |frames_passed_| and |callback_|.
    class ReadFramesStub {
    public:
        explicit ReadFramesStub(int result)
            : result_(result)
        {
        }

        ReadFramesStub(int result,
            std::vector<std::unique_ptr<WebSocketFrame>>* frames_to_output)
            : result_(result)
        {
            frames_to_output_.swap(*frames_to_output);
        }

        int Call(std::vector<std::unique_ptr<WebSocketFrame>>* frames,
            const CompletionCallback& callback)
        {
            DCHECK(frames->empty());
            frames_passed_ = frames;
            callback_ = callback;
            frames->swap(frames_to_output_);
            return result_;
        }

        int result() const { return result_; }
        const CompletionCallback& callback() const { return callback_; }
        std::vector<std::unique_ptr<WebSocketFrame>>* frames_passed()
        {
            return frames_passed_;
        }

    private:
        int result_;
        CompletionCallback callback_;
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output_;
        std::vector<std::unique_ptr<WebSocketFrame>>* frames_passed_;
    };

    // WriteFramesStub is a stub for WebSocketStream::WriteFrames.
    // It returns |result_| and |frames_| to the caller and
    // saves |callback| parameter to |callback_|.
    class WriteFramesStub {
    public:
        explicit WriteFramesStub(WebSocketDeflatePredictorMock* predictor,
            int result)
            : result_(result)
            , predictor_(predictor)
        {
        }

        int Call(std::vector<std::unique_ptr<WebSocketFrame>>* frames,
            const CompletionCallback& callback)
        {
            frames_.insert(frames_.end(), std::make_move_iterator(frames->begin()),
                std::make_move_iterator(frames->end()));
            frames->clear();
            callback_ = callback;
            predictor_->VerifySentFrames(frames_);
            return result_;
        }

        int result() const { return result_; }
        const CompletionCallback& callback() const { return callback_; }
        std::vector<std::unique_ptr<WebSocketFrame>>* frames() { return &frames_; }

    private:
        int result_;
        CompletionCallback callback_;
        std::vector<std::unique_ptr<WebSocketFrame>> frames_;
        WebSocketDeflatePredictorMock* predictor_;
    };

    TEST_F(WebSocketDeflateStreamTest, ReadFailedImmediately)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        CompletionCallback callback;
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Return(ERR_FAILED));
        }
        EXPECT_EQ(ERR_FAILED, deflate_stream_->ReadFrames(&frames, callback));
    }

    TEST_F(WebSocketDeflateStreamTest, ReadUncompressedFrameImmediately)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal,
            "hello");
        ReadFramesStub stub(OK, &frames_to_output);
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        CompletionCallback callback;
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(1u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("hello", ToString(frames[0]));
    }

    TEST_F(WebSocketDeflateStreamTest, ReadUncompressedFrameAsync)
    {
        ReadFramesStub stub(ERR_IO_PENDING);
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        MockCallback mock_callback, checkpoint;
        CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback));

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
            EXPECT_CALL(checkpoint, Call(0));
            EXPECT_CALL(mock_callback, Call(OK));
        }
        ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(0u, frames.size());

        checkpoint.Call(0);

        AppendTo(stub.frames_passed(),
            WebSocketFrameHeader::kOpCodeText,
            kFinal,
            "hello");
        stub.callback().Run(OK);
        ASSERT_EQ(1u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("hello", ToString(frames[0]));
    }

    TEST_F(WebSocketDeflateStreamTest, ReadFailedAsync)
    {
        ReadFramesStub stub(ERR_IO_PENDING);
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        MockCallback mock_callback, checkpoint;
        CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback));

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
            EXPECT_CALL(checkpoint, Call(0));
            EXPECT_CALL(mock_callback, Call(ERR_FAILED));
        }
        ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(0u, frames.size());

        checkpoint.Call(0);

        AppendTo(stub.frames_passed(),
            WebSocketFrameHeader::kOpCodeText,
            kFinal,
            "hello");
        stub.callback().Run(ERR_FAILED);
        ASSERT_EQ(0u, frames.size());
    }

    TEST_F(WebSocketDeflateStreamTest, ReadCompressedFrameImmediately)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal | kReserved1,
            std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7));
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(1u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("Hello", ToString(frames[0]));
    }

    TEST_F(WebSocketDeflateStreamTest, ReadCompressedFrameAsync)
    {
        ReadFramesStub stub(ERR_IO_PENDING);
        MockCallback mock_callback, checkpoint;
        CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback));
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
            EXPECT_CALL(checkpoint, Call(0));
            EXPECT_CALL(mock_callback, Call(OK));
        }
        ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback));

        checkpoint.Call(0);

        AppendTo(stub.frames_passed(),
            WebSocketFrameHeader::kOpCodeText,
            kFinal | kReserved1,
            std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7));
        stub.callback().Run(OK);

        ASSERT_EQ(1u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("Hello", ToString(frames[0]));
    }

    TEST_F(WebSocketDeflateStreamTest,
        ReadCompressedFrameFragmentImmediatelyButInflaterReturnsPending)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        const std::string data1("\xf2", 1);
        const std::string data2("\x48\xcd\xc9\xc9\x07\x00", 6);
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kReserved1,
            data1);
        ReadFramesStub stub1(OK, &frames_to_output), stub2(ERR_IO_PENDING);
        MockCallback mock_callback, checkpoint;
        CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback));
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub1, &ReadFramesStub::Call))
                .WillOnce(Invoke(&stub2, &ReadFramesStub::Call));
            EXPECT_CALL(checkpoint, Call(0));
            EXPECT_CALL(mock_callback, Call(OK));
        }
        ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(0u, frames.size());

        AppendTo(stub2.frames_passed(),
            WebSocketFrameHeader::kOpCodeText,
            kFinal,
            data2);

        checkpoint.Call(0);
        stub2.callback().Run(OK);

        ASSERT_EQ(1u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("Hello", ToString(frames[0]));
    }

    TEST_F(WebSocketDeflateStreamTest, ReadInvalidCompressedPayload)
    {
        const std::string data("\xf2\x48\xcdINVALID", 10);
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal | kReserved1,
            data);
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(ERR_WS_PROTOCOL_ERROR,
            deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(0u, frames.size());
    }

    TEST_F(WebSocketDeflateStreamTest, MergeMultipleFramesInReadFrames)
    {
        const std::string data1("\xf2\x48\xcd", 3);
        const std::string data2("\xc9\xc9\x07\x00", 4);
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kReserved1,
            data1);
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeContinuation,
            kFinal,
            data2);
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(1u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("Hello", ToString(frames[0]));
    }

    TEST_F(WebSocketDeflateStreamTest, ReadUncompressedEmptyFrames)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kNoFlag);
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeContinuation,
            kFinal);
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(2u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_FALSE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("", ToString(frames[0]));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
            frames[1]->header.opcode);
        EXPECT_TRUE(frames[1]->header.final);
        EXPECT_FALSE(frames[1]->header.reserved1);
        EXPECT_EQ("", ToString(frames[1]));
    }

    TEST_F(WebSocketDeflateStreamTest, ReadCompressedEmptyFrames)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kReserved1,
            std::string("\x02\x00", 1));
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeContinuation,
            kFinal);
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(1u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("", ToString(frames[0]));
    }

    TEST_F(WebSocketDeflateStreamTest,
        ReadCompressedFrameFollowedByEmptyFrame)
    {
        const std::string data("\xf2\x48\xcd\xc9\xc9\x07\x00", 7);
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kReserved1,
            data);
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeContinuation,
            kFinal);
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(1u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("Hello", ToString(frames[0]));
    }

    TEST_F(WebSocketDeflateStreamTest, ReadControlFrameBetweenDataFrames)
    {
        const std::string data1("\xf2\x48\xcd", 3);
        const std::string data2("\xc9\xc9\x07\x00", 4);
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kReserved1,
            data1);
        AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodePing, kFinal);
        AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal, data2);
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(2u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodePing, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[1]->header.opcode);
        EXPECT_TRUE(frames[1]->header.final);
        EXPECT_FALSE(frames[1]->header.reserved1);
        EXPECT_EQ("Hello", ToString(frames[1]));
    }

    TEST_F(WebSocketDeflateStreamTest, SplitToMultipleFramesInReadFrames)
    {
        WebSocketDeflater deflater(WebSocketDeflater::TAKE_OVER_CONTEXT);
        deflater.Initialize(kWindowBits);
        const size_t kSize = kChunkSize * 3;
        const std::string original_data(kSize, 'a');
        deflater.AddBytes(original_data.data(), original_data.size());
        deflater.Finish();

        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeBinary,
            kFinal | kReserved1,
            ToString(deflater.GetOutput(deflater.CurrentOutputSize())));

        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }

        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(3u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeBinary, frames[0]->header.opcode);
        EXPECT_FALSE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ(kChunkSize, static_cast<size_t>(frames[0]->header.payload_length));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
            frames[1]->header.opcode);
        EXPECT_FALSE(frames[1]->header.final);
        EXPECT_FALSE(frames[1]->header.reserved1);
        EXPECT_EQ(kChunkSize, static_cast<size_t>(frames[1]->header.payload_length));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
            frames[2]->header.opcode);
        EXPECT_TRUE(frames[2]->header.final);
        EXPECT_FALSE(frames[2]->header.reserved1);
        EXPECT_EQ(kChunkSize, static_cast<size_t>(frames[2]->header.payload_length));
        EXPECT_EQ(original_data,
            ToString(frames[0]) + ToString(frames[1]) + ToString(frames[2]));
    }

    TEST_F(WebSocketDeflateStreamTest, InflaterInternalDataCanBeEmpty)
    {
        WebSocketDeflater deflater(WebSocketDeflater::TAKE_OVER_CONTEXT);
        deflater.Initialize(kWindowBits);
        const std::string original_data(kChunkSize, 'a');
        deflater.AddBytes(original_data.data(), original_data.size());
        deflater.Finish();

        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeBinary,
            kReserved1,
            ToString(deflater.GetOutput(deflater.CurrentOutputSize())));
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeBinary,
            kFinal,
            "");

        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }

        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(2u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeBinary, frames[0]->header.opcode);
        EXPECT_FALSE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ(kChunkSize, static_cast<size_t>(frames[0]->header.payload_length));

        EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
            frames[1]->header.opcode);
        EXPECT_TRUE(frames[1]->header.final);
        EXPECT_FALSE(frames[1]->header.reserved1);
        EXPECT_EQ(0u, static_cast<size_t>(frames[1]->header.payload_length));
        EXPECT_EQ(original_data, ToString(frames[0]) + ToString(frames[1]));
    }

    TEST_F(WebSocketDeflateStreamTest,
        Reserved1TurnsOnDuringReadingCompressedContinuationFrame)
    {
        const std::string data1("\xf2\x48\xcd", 3);
        const std::string data2("\xc9\xc9\x07\x00", 4);
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kReserved1,
            data1);
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeContinuation,
            kFinal | kReserved1,
            data2);
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(ERR_WS_PROTOCOL_ERROR,
            deflate_stream_->ReadFrames(&frames, callback));
    }

    TEST_F(WebSocketDeflateStreamTest,
        Reserved1TurnsOnDuringReadingUncompressedContinuationFrame)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kNoFlag,
            "hello");
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeContinuation,
            kFinal | kReserved1,
            "world");
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(ERR_WS_PROTOCOL_ERROR,
            deflate_stream_->ReadFrames(&frames, callback));
    }

    TEST_F(WebSocketDeflateStreamTest, ReadCompressedMessages)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal | kReserved1,
            std::string(
                "\x4a\xce\xcf\x2d\x28\x4a\x2d\x2e\x4e\x4d\x31\x04\x00", 13));
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal | kReserved1,
            std::string("\x4a\x86\x33\x8d\x00\x00", 6));
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(2u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("compressed1", ToString(frames[0]));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[1]->header.opcode);
        EXPECT_TRUE(frames[1]->header.final);
        EXPECT_FALSE(frames[1]->header.reserved1);
        EXPECT_EQ("compressed2", ToString(frames[1]));
    }

    TEST_F(WebSocketDeflateStreamTest, ReadUncompressedMessages)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal,
            "uncompressed1");
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal,
            "uncompressed2");
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(2u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("uncompressed1", ToString(frames[0]));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[1]->header.opcode);
        EXPECT_TRUE(frames[1]->header.final);
        EXPECT_FALSE(frames[1]->header.reserved1);
        EXPECT_EQ("uncompressed2", ToString(frames[1]));
    }

    TEST_F(WebSocketDeflateStreamTest,
        ReadCompressedMessageThenUncompressedMessage)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal | kReserved1,
            std::string(
                "\x4a\xce\xcf\x2d\x28\x4a\x2d\x2e\x4e\x4d\x01\x00", 12));
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal,
            "uncompressed");
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(2u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("compressed", ToString(frames[0]));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[1]->header.opcode);
        EXPECT_TRUE(frames[1]->header.final);
        EXPECT_FALSE(frames[1]->header.reserved1);
        EXPECT_EQ("uncompressed", ToString(frames[1]));
    }

    TEST_F(WebSocketDeflateStreamTest,
        ReadUncompressedMessageThenCompressedMessage)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames_to_output;
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal,
            "uncompressed");
        AppendTo(&frames_to_output,
            WebSocketFrameHeader::kOpCodeText,
            kFinal | kReserved1,
            std::string(
                "\x4a\xce\xcf\x2d\x28\x4a\x2d\x2e\x4e\x4d\x01\x00", 12));
        ReadFramesStub stub(OK, &frames_to_output);
        CompletionCallback callback;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
        ASSERT_EQ(2u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_TRUE(frames[0]->header.final);
        EXPECT_FALSE(frames[0]->header.reserved1);
        EXPECT_EQ("uncompressed", ToString(frames[0]));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[1]->header.opcode);
        EXPECT_TRUE(frames[1]->header.final);
        EXPECT_FALSE(frames[1]->header.reserved1);
        EXPECT_EQ("compressed", ToString(frames[1]));
    }

    // This is a regression test for crbug.com/343506.
    TEST_F(WebSocketDeflateStreamTest, ReadEmptyAsyncFrame)
    {
        std::vector<std::unique_ptr<ReadFramesStub>> stub_vector;
        stub_vector.push_back(base::WrapUnique(new ReadFramesStub(ERR_IO_PENDING)));
        stub_vector.push_back(base::WrapUnique(new ReadFramesStub(ERR_IO_PENDING)));
        MockCallback mock_callback;
        CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback));
        std::vector<std::unique_ptr<WebSocketFrame>> frames;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(stub_vector[0].get(), &ReadFramesStub::Call));

            EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
                .WillOnce(Invoke(stub_vector[1].get(), &ReadFramesStub::Call));

            EXPECT_CALL(mock_callback, Call(OK));
        }

        ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback));
        AppendTo(stub_vector[0]->frames_passed(),
            WebSocketFrameHeader::kOpCodeText,
            kReserved1,
            std::string());
        stub_vector[0]->callback().Run(OK);
        AppendTo(stub_vector[1]->frames_passed(),
            WebSocketFrameHeader::kOpCodeContinuation,
            kFinal,
            std::string("\x02\x00"));
        stub_vector[1]->callback().Run(OK);
        ASSERT_EQ(1u, frames.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
        EXPECT_EQ("", ToString(frames[0]));
    }

    TEST_F(WebSocketDeflateStreamTest, WriteEmpty)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        CompletionCallback callback;
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)).Times(0);
        }
        EXPECT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback));
    }

    TEST_F(WebSocketDeflateStreamTest, WriteFailedImmediately)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        CompletionCallback callback;
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _))
                .WillOnce(Return(ERR_FAILED));
        }

        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "hello");
        predictor_->AddFramesToBeInput(frames);
        EXPECT_EQ(ERR_FAILED, deflate_stream_->WriteFrames(&frames, callback));
        predictor_->Clear();
    }

    TEST_F(WebSocketDeflateStreamTest, WriteFrameImmediately)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        CompletionCallback callback;
        WriteFramesStub stub(predictor_, OK);
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello");
        predictor_->AddFramesToBeInput(frames);
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(_, _))
                .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback));
        const std::vector<std::unique_ptr<WebSocketFrame>>& frames_passed = *stub.frames();
        ASSERT_EQ(1u, frames_passed.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode);
        EXPECT_TRUE(frames_passed[0]->header.final);
        EXPECT_TRUE(frames_passed[0]->header.reserved1);
        EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7),
            ToString(frames_passed[0]));
    }

    TEST_F(WebSocketDeflateStreamTest, WriteFrameAsync)
    {
        WriteFramesStub stub(predictor_, ERR_IO_PENDING);
        MockCallback mock_callback, checkpoint;
        CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback));
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _))
                .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
            EXPECT_CALL(checkpoint, Call(0));
            EXPECT_CALL(mock_callback, Call(OK));
        }
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello");
        predictor_->AddFramesToBeInput(frames);
        ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->WriteFrames(&frames, callback));

        checkpoint.Call(0);
        stub.callback().Run(OK);

        const std::vector<std::unique_ptr<WebSocketFrame>>& frames_passed = *stub.frames();
        ASSERT_EQ(1u, frames_passed.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode);
        EXPECT_TRUE(frames_passed[0]->header.final);
        EXPECT_TRUE(frames_passed[0]->header.reserved1);
        EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7),
            ToString(frames_passed[0]));
    }

    TEST_F(WebSocketDeflateStreamTest, WriteControlFrameBetweenDataFrames)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "Hel");
        AppendTo(&frames, WebSocketFrameHeader::kOpCodePing, kFinal);
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "lo");
        predictor_->AddFramesToBeInput(frames);
        WriteFramesStub stub(predictor_, OK);
        CompletionCallback callback;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _))
                .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback));
        const std::vector<std::unique_ptr<WebSocketFrame>>& frames_passed = *stub.frames();
        ASSERT_EQ(2u, frames_passed.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodePing, frames_passed[0]->header.opcode);
        EXPECT_TRUE(frames_passed[0]->header.final);
        EXPECT_FALSE(frames_passed[0]->header.reserved1);
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[1]->header.opcode);
        EXPECT_TRUE(frames_passed[1]->header.final);
        EXPECT_TRUE(frames_passed[1]->header.reserved1);
        EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7),
            ToString(frames_passed[1]));
    }

    TEST_F(WebSocketDeflateStreamTest, WriteEmptyMessage)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal);
        predictor_->AddFramesToBeInput(frames);
        WriteFramesStub stub(predictor_, OK);
        CompletionCallback callback;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _))
                .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback));
        const std::vector<std::unique_ptr<WebSocketFrame>>& frames_passed = *stub.frames();
        ASSERT_EQ(1u, frames_passed.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode);
        EXPECT_TRUE(frames_passed[0]->header.final);
        EXPECT_TRUE(frames_passed[0]->header.reserved1);
        EXPECT_EQ(std::string("\x00", 1), ToString(frames_passed[0]));
    }

    TEST_F(WebSocketDeflateStreamTest, WriteUncompressedMessage)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "AAAA");
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "AAA");
        predictor_->AddFramesToBeInput(frames);
        WriteFramesStub stub(predictor_, OK);
        CompletionCallback callback;

        predictor_->set_result(WebSocketDeflatePredictor::DO_NOT_DEFLATE);

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _))
                .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback));
        const std::vector<std::unique_ptr<WebSocketFrame>>& frames_passed = *stub.frames();
        ASSERT_EQ(2u, frames_passed.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode);
        EXPECT_FALSE(frames_passed[0]->header.final);
        EXPECT_FALSE(frames_passed[0]->header.reserved1);
        EXPECT_EQ("AAAA", ToString(frames_passed[0]));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
            frames_passed[1]->header.opcode);
        EXPECT_TRUE(frames_passed[1]->header.final);
        EXPECT_FALSE(frames_passed[1]->header.reserved1);
        EXPECT_EQ("AAA", ToString(frames_passed[1]));
    }

    TEST_F(WebSocketDeflateStreamTest, LargeDeflatedFramesShouldBeSplit)
    {
        WebSocketDeflater deflater(WebSocketDeflater::TAKE_OVER_CONTEXT);
        LinearCongruentialGenerator lcg(133);
        WriteFramesStub stub(predictor_, OK);
        CompletionCallback callback;
        const size_t size = 1024;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(_, _))
                .WillRepeatedly(Invoke(&stub, &WriteFramesStub::Call));
        }
        std::vector<std::unique_ptr<WebSocketFrame>> total_compressed_frames;

        deflater.Initialize(kWindowBits);
        while (true) {
            bool is_final = (total_compressed_frames.size() >= 2);
            std::vector<std::unique_ptr<WebSocketFrame>> frames;
            std::string data;
            for (size_t i = 0; i < size; ++i)
                data += static_cast<char>(lcg.Generate());
            deflater.AddBytes(data.data(), data.size());
            FrameFlag flag = is_final ? kFinal : kNoFlag;
            AppendTo(&frames, WebSocketFrameHeader::kOpCodeBinary, flag, data);
            predictor_->AddFramesToBeInput(frames);
            ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback));
            total_compressed_frames.insert(
                total_compressed_frames.end(),
                std::make_move_iterator(stub.frames()->begin()),
                std::make_move_iterator(stub.frames()->end()));
            stub.frames()->clear();
            if (is_final)
                break;
        }
        deflater.Finish();
        std::string total_deflated;
        for (size_t i = 0; i < total_compressed_frames.size(); ++i) {
            WebSocketFrame* frame = total_compressed_frames[i].get();
            const WebSocketFrameHeader& header = frame->header;
            if (i > 0) {
                EXPECT_EQ(header.kOpCodeContinuation, header.opcode);
                EXPECT_FALSE(header.reserved1);
            } else {
                EXPECT_EQ(header.kOpCodeBinary, header.opcode);
                EXPECT_TRUE(header.reserved1);
            }
            const bool is_final_frame = (i + 1 == total_compressed_frames.size());
            EXPECT_EQ(is_final_frame, header.final);
            if (!is_final_frame)
                EXPECT_GT(header.payload_length, 0ul);
            total_deflated += ToString(frame);
        }
        EXPECT_EQ(total_deflated,
            ToString(deflater.GetOutput(deflater.CurrentOutputSize())));
    }

    TEST_F(WebSocketDeflateStreamTest, WriteMultipleMessages)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello");
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello");
        predictor_->AddFramesToBeInput(frames);
        WriteFramesStub stub(predictor_, OK);
        CompletionCallback callback;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _))
                .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback));
        const std::vector<std::unique_ptr<WebSocketFrame>>& frames_passed = *stub.frames();
        ASSERT_EQ(2u, frames_passed.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode);
        EXPECT_TRUE(frames_passed[0]->header.final);
        EXPECT_TRUE(frames_passed[0]->header.reserved1);
        EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7),
            ToString(frames_passed[0]));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[1]->header.opcode);
        EXPECT_TRUE(frames_passed[1]->header.final);
        EXPECT_TRUE(frames_passed[1]->header.reserved1);
        EXPECT_EQ(std::string("\xf2\x00\x11\x00\x00", 5), ToString(frames_passed[1]));
    }

    TEST_F(WebSocketDeflateStreamWithDoNotTakeOverContextTest,
        WriteMultipleMessages)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello");
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello");
        predictor_->AddFramesToBeInput(frames);
        WriteFramesStub stub(predictor_, OK);
        CompletionCallback callback;

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _))
                .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback));
        const std::vector<std::unique_ptr<WebSocketFrame>>& frames_passed = *stub.frames();
        ASSERT_EQ(2u, frames_passed.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode);
        EXPECT_TRUE(frames_passed[0]->header.final);
        EXPECT_TRUE(frames_passed[0]->header.reserved1);
        EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7),
            ToString(frames_passed[0]));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[1]->header.opcode);
        EXPECT_TRUE(frames_passed[1]->header.final);
        EXPECT_TRUE(frames_passed[1]->header.reserved1);
        EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7),
            ToString(frames_passed[1]));
    }

    // In order to check the stream works correctly for multiple
    // "PossiblyCompressedMessage"s, we test various messages at one test case.
    TEST_F(WebSocketDeflateStreamWithDoNotTakeOverContextTest,
        WritePossiblyCompressMessages)
    {
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "He");
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "llo");
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "AAAAAAAAAA");
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "AA");
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "XX");
        AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "YY");
        predictor_->AddFramesToBeInput(frames);
        WriteFramesStub stub(predictor_, OK);
        CompletionCallback callback;
        predictor_->set_result(WebSocketDeflatePredictor::TRY_DEFLATE);

        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _))
                .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback));
        const std::vector<std::unique_ptr<WebSocketFrame>>& frames_passed = *stub.frames();
        ASSERT_EQ(5u, frames_passed.size());

        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode);
        EXPECT_FALSE(frames_passed[0]->header.final);
        EXPECT_FALSE(frames_passed[0]->header.reserved1);
        EXPECT_EQ("He", ToString(frames_passed[0]));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
            frames_passed[1]->header.opcode);
        EXPECT_TRUE(frames_passed[1]->header.final);
        EXPECT_FALSE(frames_passed[1]->header.reserved1);
        EXPECT_EQ("llo", ToString(frames_passed[1]));

        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[2]->header.opcode);
        EXPECT_TRUE(frames_passed[2]->header.final);
        EXPECT_TRUE(frames_passed[2]->header.reserved1);
        EXPECT_EQ(std::string("\x72\x74\x44\x00\x00\x00", 6),
            ToString(frames_passed[2]));

        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[3]->header.opcode);
        EXPECT_FALSE(frames_passed[3]->header.final);
        EXPECT_FALSE(frames_passed[3]->header.reserved1);
        EXPECT_EQ("XX", ToString(frames_passed[3]));
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
            frames_passed[4]->header.opcode);
        EXPECT_TRUE(frames_passed[4]->header.final);
        EXPECT_FALSE(frames_passed[4]->header.reserved1);
        EXPECT_EQ("YY", ToString(frames_passed[4]));
    }

    // This is based on the similar test from websocket_deflater_test.cc
    TEST_F(WebSocketDeflateStreamWithClientWindowBitsTest, WindowBits8)
    {
        SetUpWithWindowBits(8);
        CompletionCallback callback;
        AddCompressibleFrameString();
        WriteFramesStub stub(predictor_, OK);
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(_, _))
                .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames_, callback));
        const std::vector<std::unique_ptr<WebSocketFrame>>& frames_passed = *stub.frames();
        ASSERT_EQ(1u, frames_passed.size());
        EXPECT_EQ(std::string("r\xce(\xca\xcf\xcd,\xcdM\x1c\xe1\xc0\x39\xa3"
                              "(?7\xb3\x34\x17\x00",
                      21),
            ToString(frames_passed[0]));
    }

    // The same input with window_bits=10 returns smaller output.
    TEST_F(WebSocketDeflateStreamWithClientWindowBitsTest, WindowBits10)
    {
        SetUpWithWindowBits(10);
        CompletionCallback callback;
        AddCompressibleFrameString();
        WriteFramesStub stub(predictor_, OK);
        {
            InSequence s;
            EXPECT_CALL(*mock_stream_, WriteFrames(_, _))
                .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
        }
        ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames_, callback));
        const std::vector<std::unique_ptr<WebSocketFrame>>& frames_passed = *stub.frames();
        ASSERT_EQ(1u, frames_passed.size());
        EXPECT_EQ(
            std::string("r\xce(\xca\xcf\xcd,\xcdM\x1c\xe1\xc0\x19\x1a\x0e\0\0", 17),
            ToString(frames_passed[0]));
    }

} // namespace

} // namespace net
